﻿/*  ------------------------------------------------------------------------
	NQ Common Services Library
	
	Homepage: http://www.awzhome.de/
	------------------------------------------------------------------------
	
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License. You may obtain a copy of the License at
	http://www.mozilla.org/MPL/
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
   
	The Original Code is code of NQ Common Services Library.

	The Initial Developer of the Original Code is Andreas Weizel,
	based on code examples provided by Paul Stovell.
	Portions created by the Initial Developer are
	Copyright (C) 2012 Andreas Weizel. All Rights Reserved.
	Code examples by Paul Stovell are available at
	http://www.paulstovell.com/wpf-delaybinding
	and are subject to public domain.

	Contributor(s): (none)
	
	Alternatively, the contents of this file may be used under the terms
	of the GNU General Public License Version 2.0 or later (the "GPL"),
	or the GNU Lesser General Public License Version 2.1 or later (the
	"LGPL"), in which case the provisions of the GPL or the LGPL are
	applicable instead of those above. If you wish to allow use of your
	version of this file only under the terms of the GPL or the LGPL and
	not to allow others to use your version of this file under the MPL,
	indicate your decision by deleting the provisions above and replace
	them with the notice and other provisions required by the GPL or the
	LGPL. If you do not delete the provisions above, a recipient may use
	your version of this file under the terms of any of the MPL, the GPL
	or the LGPL.
	------------------------------------------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using AWZhome.NQ.Core;
using System.Windows.Data;
using System.Windows;
using System.ComponentModel;
using System.Globalization;
using AWZhome.NQ.Core.Fluent;
using System.Windows.Media;

namespace AWZhome.NQ.CommonServices
{
	/// <summary>
	/// Provides a possibility to use bind WPF control properties directly
	/// to configuration values.
	/// </summary>
	[MarkupExtensionReturnType(typeof(object))]
	public class ConfigBindingExtension : MarkupExtension
	{
		/// <summary>
		/// Helper class to assign a configuration binding to a dependency property.
		/// </summary>
		private class ConfigBindingHelper
		{
			DependencyObject _bindingTarget;
			DependencyPropertyDescriptor _descriptor;
			string _category;
			string _group;
			string _value;
			string _list;
			Type _type;
			bool _userSetting;

			public ConfigBindingHelper(
				DependencyObject bindingTarget, DependencyProperty bindingTargetProperty,
				string category, string group, string value, string list, Type type, bool userSetting)
			{
				_bindingTarget = bindingTarget;
				_category = category;
				_group = group;
				_value = value;
				_list = list;
				_type = type;
				_userSetting = userSetting;
				_descriptor = DependencyPropertyDescriptor.FromProperty(bindingTargetProperty, bindingTarget.GetType());
				_descriptor.AddValueChanged(bindingTarget, BindingTarget_TargetPropertyChanged);
			}

			private void BindingTarget_TargetPropertyChanged(object sender, EventArgs e)
			{
				// Get instance of configuration service
				IConfigManager configService = null;
				if (NQServiceManager.Instance != null)
				{
					configService = ServiceCore
						.Get<Services.IConfigService>()
						.Instance;
				}
				if (configService != null)
				{
					// Set configuration value, typed according to Type property
					object value = _descriptor.GetValue(_bindingTarget);
					if (value != null)
					{
						if (_value != null)
						{
							ConfigData configData = new ConfigData();
							if (_type == typeof(bool))
							{
								configData.FromBoolean((bool) value);
							}
							else if (_type == typeof(Color))
							{
								configData.FromColor((Color) value);
							}
							else if (_type == typeof(double))
							{
								configData.FromDouble((double) value);
							}
							else if (_type == typeof(Int32))
							{
								configData.FromInt32((Int32) value);
							}
							else if (_type == typeof(Int64))
							{
								configData.FromInt64((Int64) value);
							}
							else if (_type == typeof(Point))
							{
								configData.FromPoint((Point) value);
							}
							else if (_type == typeof(Rect))
							{
								configData.FromRect((Rect) value);
							}
							else if (_type == typeof(string[]))
							{
								configData.FromStringList((string[]) value);
							}
							else
							{
								configData.FromString((string) value);
							}

							// Save as single value
							configService.SetSetting(_category, _group, _value, configData, _userSetting);
						}
						else if (_list != null)
						{
							// Save as list setting
							configService.SetListSetting(_category, _group, _list, (IList<ConfigData>) value, _userSetting);
						}
					}
				}
			}
		}

		/// <summary>
		/// Constructor of <see cref="AWZhome.NQ.CommonServices.ConfigBindingExtension" />.
		/// </summary>
		public ConfigBindingExtension()
		{
			UserSetting = true;
		}

		public override object ProvideValue(IServiceProvider serviceProvider)
		{
			var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
			if (valueProvider != null)
			{
				var bindingTarget = valueProvider.TargetObject as DependencyObject;
				var bindingProperty = valueProvider.TargetProperty as DependencyProperty;
				if ((bindingProperty == null) || (bindingTarget == null))
				{
					throw new NotSupportedException(string.Format(
						"The property '{0}' on target '{1}' is not valid for a NQConfigBinding. The NQConfigBinding target must be a DependencyObject, "
						+ "and the target property must be a DependencyProperty.",
						valueProvider.TargetProperty,
						valueProvider.TargetObject));
				}

				// Create instance of helper object. This object will live as long as the target element lives, since it subscribes to the changing event, 
				// and will be garbage collected as soon as the element isn't required (e.g., when it's Window closes).
				new ConfigBindingHelper(bindingTarget, bindingProperty, Category, Group, Value, List, Type, UserSetting);

				// Get instance of configuration service
				IConfigManager configService = null;
				if (NQServiceManager.Instance != null)
				{
					configService = ServiceCore
						.Get<Services.IConfigService>()
						.Instance;
				}
				if (configService != null)
				{
					if (Value != null)
					{
						ConfigData configData = configService.GetSetting(Category, Group, Value);
						if (configData != null)
						{
							// Convert the value according to Type property (otherwise the RawData is used)
							if (Type == typeof(bool))
							{
								return configData.AsBoolean(false);
							}
							else if (Type == typeof(Color))
							{
								return configData.AsColor(Colors.Transparent);
							}
							else if (Type == typeof(double))
							{
								return configData.AsDouble(0f);
							}
							else if (Type == typeof(Int32))
							{
								return configData.AsInt32(0);
							}
							else if (Type == typeof(Int64))
							{
								return configData.AsInt64(0);
							}
							else if (Type == typeof(Point))
							{
								return configData.AsPoint(new Point(0, 0));
							}
							else if (Type == typeof(Rect))
							{
								return configData.AsRect(new Rect(0, 0, 0, 0));
							}
							else if (Type == typeof(string[]))
							{
								return configData.AsStringList(new string[] { });
							}

							return configData.RawData;
						}
					}
					else if (List != null)
					{
						// Return setting list
						return configService.GetListSetting(Category, Group, List);
					}
				}
			}

			return null;
		}

		/// <summary>
		/// Gets/sets the configuration category name to use.
		/// </summary>
		public string Category
		{
			get;
			set;
		}

		/// <summary>
		/// Gets/sets the group name to use.
		/// </summary>
		public string Group
		{
			get;
			set;
		}

		/// <summary>
		/// Gets/sets the value name, if a value is to be used.
		/// </summary>
		public string Value
		{
			get;
			set;
		}

		/// <summary>
		/// Gets/sets the list name, if a list should be used.
		/// </summary>
		public string List
		{
			get;
			set;
		}

		/// <summary>
		/// Gets/sets the type, which to convert the value to.
		/// </summary>
		public Type Type
		{
			get;
			set;
		}

		/// <summary>
		/// Determines whether the setting should be saved as user setting or globally.
		/// </summary>
		public bool UserSetting
		{
			get;
			set;
		}
	}
}
